# -*- mode: ruby; coding: us-ascii -*-
firstline, predefined = __LINE__+1, %[\
  max
  min
  hash
  freeze
  nil?
  inspect
  intern
  object_id
  const_added
  const_missing
  method_missing                                        MethodMissing
  method_added
  singleton_method_added
  method_removed
  singleton_method_removed
  method_undefined
  singleton_method_undefined
  length
  size
  gets
  succ
  each
  proc
  lambda
  send
  __send__
  __recursive_key__
  initialize
  initialize_copy
  initialize_clone
  initialize_dup
  to_int
  to_ary
  to_str
  to_sym
  to_hash
  to_proc
  to_io
  to_a
  to_s
  to_i
  to_f
  to_r
  bt
  bt_locations
  call
  mesg
  exception
  locals
  not                                                   NOT
  and                                                   AND
  or                                                    OR
  div
  divmod
  fdiv
  quo
  name
  nil
  path

  _                                                     UScore

  # MUST be successive
  _1  NUMPARAM_1
  _2  NUMPARAM_2
  _3  NUMPARAM_3
  _4  NUMPARAM_4
  _5  NUMPARAM_5
  _6  NUMPARAM_6
  _7  NUMPARAM_7
  _8  NUMPARAM_8
  _9  NUMPARAM_9

  "/*NULL*/"                                            NULL
  empty?
  eql?
  default
  respond_to?                                           Respond_to
  respond_to_missing?                                   Respond_to_missing
  <IFUNC>
  <CFUNC>
  core#set_method_alias
  core#set_variable_alias
  core#undef_method
  core#define_method
  core#define_singleton_method
  core#set_postexe
  core#hash_merge_ptr
  core#hash_merge_kwd
  core#raise
  core#sprintf

  -                                                     debug#created_info

  $_                                                    LASTLINE
  $~                                                    BACKREF
  $!                                                    ERROR_INFO
]

# VM ID         OP      Parser Token
token_ops = %[\
  Dot2          ..      DOT2
  Dot3          ...     DOT3
  BDot2         ..      BDOT2
  BDot3         ...     BDOT3
  UPlus         +@      UPLUS
  UMinus        -@      UMINUS
  Pow           **      POW
  Cmp           <=>     CMP
  PLUS          +
  MINUS         -
  MULT          *
  DIV           /
  MOD           %
  LTLT          <<      LSHFT
  GTGT          >>      RSHFT
  LT            <
  LE            <=      LEQ
  GT            >
  GE            >=      GEQ
  Eq            ==      EQ
  Eqq           ===     EQQ
  Neq           !=      NEQ
  Not           !
  And           &
  Or            |
  Backquote     `
  EqTilde       =~      MATCH
  NeqTilde      !~      NMATCH
  AREF          []
  ASET          []=
  COLON2        ::
  ANDOP         &&
  OROP          ||
  ANDDOT        &.
]

class KeywordError < RuntimeError
  def self.raise(mesg, line)
    super(self, mesg, ["#{__FILE__}:#{line}", *caller])
  end
end

def id2varname(token, prefix = nil)
  if /#/ =~ token
    token = "_#{token.gsub(/\W+/, '_')}"
  else
    token = token.sub(/\?/, 'P')
    token = prefix + token if prefix
    token.sub!(/\A[a-z]/) {$&.upcase}
    token.sub!(/\A\$/, "_G_")
    token.sub!(/\A@@/, "_C_")
    token.sub!(/\A@/, "_I_")
    token.gsub!(/\W+/, "")
  end
  token
end

predefined_ids = {}
preserved_ids = []
local_ids = []
instance_ids = []
global_ids = []
const_ids = []
class_ids = []
attrset_ids = []
token_op_ids = []
names = {}
predefined.split(/^/).each_with_index do |line, num|
  next if /^#/ =~ line
  line.sub!(/\s+#.*/, '')
  name, token = line.split
  next unless name
  token = id2varname(token || name)
  if name == '-'
    preserved_ids << token
    next
  end
  if prev = names[name]
    KeywordError.raise("#{name} is already registered at line #{prev+firstline}", firstline+num)
  end
  if prev = predefined_ids[token]
    KeywordError.raise("#{token} is already used for #{prev} at line #{names[prev]+firstline}", firstline+num)
  end
  names[name] = num
  case name
  when /\A[A-Z]\w*\z/; const_ids
  when /\A(?!\d)\w+\z/; local_ids
  when /\A\$(?:\d+|(?!\d)\w+|\W)\z/; global_ids
  when /\A@@(?!\d)\w+\z/; class_ids
  when /\A@(?!\d)\w+\z/; instance_ids
  when /\A((?!\d)\w+)=\z/; attrset_ids
  else preserved_ids
  end << token
  predefined_ids[token] = name
end
index = 127
token_ops.split(/^/).each do |line|
  next if /^#/ =~ line
  line.sub!(/\s+#.*/, '')
  id, op, token = line.split
  next unless id and op
  token ||= (id unless /\A\W\z/ =~ op)
  token_op_ids << [id, op, token, (index += 1 if token)]
end
{
  "LOCAL" => local_ids,
  "INSTANCE" => instance_ids,
  "GLOBAL" => global_ids,
  "CONST" => const_ids,
  "CLASS" => class_ids,
  "ATTRSET" => attrset_ids,
  :preserved => preserved_ids,
  :predefined => predefined_ids,
  :token_op => token_op_ids,
  :last_token => index,
}
